/**
 * @file exercise.c
 *
 */

/*********************
*      INCLUDES
*********************/
#include "exercise.h"

#if USE_LV_WATCH_EXERCISE != 0

/*********************
*      DEFINES
*********************/

/**********************
*      TYPEDEFS
**********************/

/**********************
*  STATIC PROTOTYPES
**********************/

/**********************
*  STATIC VARIABLES
**********************/

/**********************
*      MACROS
**********************/

/**********************
 *   STATIC FUNCTIONS
 **********************/
static void exercise_add_btn_cb(lv_obj_t * btn, lv_event_t event)
{
    if(LV_EVENT_CLICKED != event) return;

    printf("%s\n", __FUNCTION__);

    EXERCISE_TYPE_T type = lv_watch_get_free_num(btn);
    printf("%s: ------------------type %d, btn %p\n", __FUNCTION__, type, btn);
    exercise_ext_t * ext = exercise_get_ext(ACT_ID_EXERCISE);
    exercise_item_t * item = _lv_ll_ins_tail(&ext->items);
    item->type = type;
    exercise_item_info_t item_info = {};
    exercise_sport_info_t sport_info = {};
    exercise_get_sport_info(type, &sport_info);
    uint32_t size = exercise_nv_init(sport_info.setting_item_bitmap, &item->data);
    exercise_write_nv(type, 0, size, item->data, EXERCISE_WRITE_NV_MODE_RESET);

    item_info.img_src = sport_info.img_src;
    item_info.img_bg_color = sport_info.img_bg_color;
    item_info.is_opacity = true;
    item_info.prim_txt_id = sport_info.txt_id;
    exercise_nv_goals_t * goals_nv
        = (exercise_nv_goals_t *)exercise_get_nv_pointer(item->data,
                                                         sport_info.setting_item_bitmap,
                                                         EXERCISE_SETTING_ITEM_TYPE_GOALS);
    exercise_get_goals_txt(goals_nv, item_info.sec_txt, EXERCISE_MAX_TXT_LEN);
    item_info.type = EXERCISE_LIST_BTN_TYPE_SETTING;
    item_info.cb = exercise_main_sport_cb;
    item_info.setting_cb = exercise_main_sport_setting_cb;
    lv_obj_t * new_btn = exercise_add_list_btn(item, ext->list, &item_info);
    lv_obj_t * scrl = lv_page_get_scrollable(ext->list);
    _lv_ll_move_before(&scrl->child_ll, new_btn, _lv_ll_get_next(&scrl->child_ll, ext->add_btn));
    scrl->signal_cb(scrl, LV_SIGNAL_CHILD_CHG, new_btn);
    lv_list_focus_btn(ext->list, new_btn);

    exercise_nv_main_t nv = {};
    uint32_t data_size;
    exercise_read_nv(EXERCISE_TYPE_NONE, (uint8_t **)(&nv), &data_size);
    Hal_Mem_Set(nv.item, 0, EXERCISE_TYPE_MAX);
    uint32_t i = 0;
    item = _lv_ll_get_head(&ext->items);
    while(item) {
        nv.item[i++] = item->type;
        item = _lv_ll_get_next(&ext->items, item);
    }
    exercise_write_nv(EXERCISE_TYPE_NONE, 0, sizeof(exercise_nv_main_t), &nv,
                      EXERCISE_WRITE_NV_MODE_RESET);

    exercise_del_ui(ACT_ID_EXERCISE_ADD);
}

static lv_obj_t * exercise_add_create(lv_obj_t * activity_obj)
{
    printf("%s\n", __FUNCTION__);

    if(NULL == activity_obj) {
        lv_watch_activity_ext_t activity_ext = {};
        activity_ext.actId = ACT_ID_EXERCISE_ADD;
        activity_ext.create = exercise_add_create;
        activity_ext.prepare_destory = exercise_com_prepare_destroy;
        activity_obj = lv_watch_creat_activity_obj(&activity_ext);
        LV_ASSERT_MEM(activity_obj);
    }

    lv_obj_t * obj = lv_watch_obj_create(activity_obj);
    LV_ASSERT_MEM(obj);

    lv_obj_t * list = exercise_add_list_ui(obj, 4);

    exercise_sport_info_t sport_info;
    exercise_item_info_t item_info = {};
    item_info.prim_txt_id = WATCH_TEXT_ID_ADD_SPORT;
    exercise_add_list_btn(NULL, list, &item_info);

    lv_obj_t * btn;
    exercise_item_t * item;
    exercise_ext_t * ext = exercise_get_ext(ACT_ID_EXERCISE);
    item_info.is_opacity = true;
    for(uint8_t i = EXERCISE_TYPE_OUTDOOR_RUNNING;i <= EXERCISE_TYPE_MAX;i++) {
        item = _lv_ll_get_head(&ext->items);
        while(item) {
            if(item->type == i) {
                break;
            }
            item = _lv_ll_get_next(&ext->items, item);
        }
        if(NULL == item) {
            exercise_get_sport_info(i, &sport_info);
            item_info.img_src = sport_info.img_src;
            item_info.img_bg_color = sport_info.img_bg_color;
            item_info.prim_txt_id = sport_info.txt_id;
            item_info.cb = exercise_add_btn_cb;
            item_info.type = EXERCISE_LIST_BTN_TYPE_NORMAL;
            btn = exercise_add_list_btn(NULL, list, &item_info);
            lv_watch_set_free_num(btn, i);
        }
    }

    exercise_add_empty_list_btn(list);

    return(obj);
}

static void exercise_add_sport_cb(lv_obj_t * btn, lv_event_t event)
{
    if(LV_EVENT_CLICKED != event) return;

    printf("%s\n", __FUNCTION__);

    exercise_add_create(NULL);
}

static void exercise_recovery_time_anim_cb(lv_anim_t * a)
{
    exercise_ext_t * ext = exercise_get_ext(ACT_ID_EXERCISE);
    uint32_t recovery_time = lv_watch_get_free_num(ext->recovery_time_label);
    if(a->time >= recovery_time) {
        recovery_time = 0;
    } else {
        recovery_time -= a->time;
    }
    lv_watch_set_free_num(ext->recovery_time_label, recovery_time);
    uint32_t hours = recovery_time / 3600;
    if(0 != (recovery_time % 3600)) hours++;

    char txt[EXERCISE_MAX_TXT_LEN] = {};
    if(1 < hours) {
        snprintf(txt, EXERCISE_MAX_TXT_LEN, "%s %d %s",
                 (char *)lv_lang_get_text(WATCH_TEXT_ID_RECOVERY_TIME), hours,
                 (char *)lv_lang_get_text(WATCH_TEXT_ID_ACT_HOURS));
    } else {
        snprintf(txt, EXERCISE_MAX_TXT_LEN, "%s %d %s",
                 (char *)lv_lang_get_text(WATCH_TEXT_ID_RECOVERY_TIME), hours,
                 (char *)lv_lang_get_text(WATCH_TEXT_ID_ACT_HOUR));
    }
    lv_label_set_text(ext->recovery_time_label, txt);
    if(0 == hours) {
        exercise_nv_main_t nv = {};
        exercise_write_nv(EXERCISE_TYPE_NONE, 0, 2 * sizeof(uint32_t), &nv,
                          EXERCISE_WRITE_NV_MODE_REPLACE);
        lv_anim_del(a->var, NULL);
    }
}

static void exercise_prepare_destroy(lv_obj_t * activity_obj)
{
    printf("%s\n", __FUNCTION__);
    exercise_ext_t * ext = exercise_get_ext(ACT_ID_EXERCISE);
    exercise_item_t * item = _lv_ll_get_head(&ext->items);
    while(item) {
        if(item->data) {
            Hal_Mem_Free(item->data);
        }
        item = _lv_ll_get_next(&ext->items, item);
    }
    _lv_ll_clear(&ext->items);
    lv_watch_png_cache_all_free();
}

static lv_obj_t * exercise_main_create(lv_obj_t * activity_obj)
{
    printf("%s\n", __FUNCTION__);

    if(NULL == activity_obj) {
        lv_watch_activity_ext_t activity_ext = {};
        activity_ext.actId = ACT_ID_EXERCISE;
        activity_ext.create = exercise_main_create;
        activity_ext.prepare_destory = exercise_prepare_destroy;
        activity_obj = lv_watch_creat_activity_obj(&activity_ext);
        LV_ASSERT_MEM(activity_obj);
    }

    lv_obj_t * obj = lv_watch_obj_create(activity_obj);
    LV_ASSERT_MEM(obj);
    exercise_ext_t * ext = lv_obj_allocate_ext_attr(obj, sizeof(exercise_ext_t));
    LV_ASSERT_MEM(ext);

    _lv_ll_init(&ext->items, sizeof(exercise_item_t));

    /* read NVM */
    exercise_nv_main_t * nv;
    uint32_t data_size;
    exercise_read_nv(EXERCISE_TYPE_NONE, (uint8_t **)(&nv), &data_size);
    if(0 == data_size) {
        data_size = sizeof(exercise_nv_main_t);
        nv = Hal_Mem_Alloc(data_size);
        Hal_Mem_Set(nv, 0, data_size);
        nv->item[0] = EXERCISE_TYPE_OUTDOOR_RUNNING;
        nv->item[1] = EXERCISE_TYPE_OUTDOOR_CYCLING;
        exercise_write_nv(EXERCISE_TYPE_NONE, 0, data_size, nv, EXERCISE_WRITE_NV_MODE_RESET);
    }

    uint32_t recovery_time = 0;
    if(0 < nv->last_exercise_end_time) {
        hal_rtc_t time;
        Hal_Rtc_Gettime(&time);
        uint32_t cur_seconds = time_to_seconds(&time);
        if(nv->last_exercise_end_time < cur_seconds) {
            uint32_t seconds = cur_seconds - nv->last_exercise_end_time;
            if(seconds >= nv->recovery_time) {
                nv->last_exercise_end_time = 0;
                nv->recovery_time = 0;
                exercise_write_nv(EXERCISE_TYPE_NONE, 0, 2 * sizeof(uint32_t), nv,
                                  EXERCISE_WRITE_NV_MODE_REPLACE);
            } else {
                seconds = nv->recovery_time - seconds;
                recovery_time = seconds / 3600;
                if(0 != (seconds % 3600)) recovery_time++;
            }
        }
    }

    ext->list = exercise_add_list_ui(obj, 4);

    exercise_item_info_t item_info = {};
    item_info.prim_txt_id = WATCH_TEXT_ID_EXERCISE;
    if(1 < recovery_time) {
        snprintf(item_info.sec_txt, EXERCISE_MAX_TXT_LEN, "%s %d %s",
                 (char *)lv_lang_get_text(WATCH_TEXT_ID_RECOVERY_TIME), recovery_time,
                 (char *)lv_lang_get_text(WATCH_TEXT_ID_ACT_HOURS));
    } else {
        snprintf(item_info.sec_txt, EXERCISE_MAX_TXT_LEN, "%s %d %s",
                 (char *)lv_lang_get_text(WATCH_TEXT_ID_RECOVERY_TIME), recovery_time,
                 (char *)lv_lang_get_text(WATCH_TEXT_ID_ACT_HOUR));
    }
    lv_obj_t * btn = exercise_add_list_btn(NULL, ext->list, &item_info);
    ext->recovery_time_label = lv_obj_get_child(btn, NULL);
    lv_watch_set_free_num(ext->recovery_time_label, nv->last_exercise_end_time);

    if(0 < recovery_time) {
        exercise_start_recovery_time_anim(ext);
    }

    uint32_t offset;
    exercise_item_t * item;
    exercise_sport_info_t sport_info;
    item_info.type = EXERCISE_LIST_BTN_TYPE_SETTING;
    item_info.cb = exercise_main_sport_cb;
    item_info.setting_cb = exercise_main_sport_setting_cb;
    item_info.is_opacity = true;
    for(uint8_t i = 0;i < EXERCISE_TYPE_MAX;i++) {
        if(EXERCISE_TYPE_NONE == nv->item[i]) break;

        item = _lv_ll_ins_tail(&ext->items);
        item->type = nv->item[i];
        exercise_read_nv(item->type, &item->data, &data_size);
        exercise_get_sport_info(item->type, &sport_info);
        if(0 == data_size) {
            data_size = exercise_nv_init(sport_info.setting_item_bitmap, &item->data);
            exercise_write_nv(item->type, 0, data_size, item->data, EXERCISE_WRITE_NV_MODE_RESET);
        }
        item_info.img_src = sport_info.img_src;
        item_info.img_bg_color = sport_info.img_bg_color;
        item_info.prim_txt_id = sport_info.txt_id;
        offset = exercise_get_nv_offset(sport_info.setting_item_bitmap,
                                        EXERCISE_SETTING_ITEM_TYPE_GOALS);
        exercise_get_goals_txt((exercise_nv_goals_t *)(&item->data[offset]), item_info.sec_txt,
                               EXERCISE_MAX_TXT_LEN);
        btn = exercise_add_list_btn(item, ext->list, &item_info);
    }

    Hal_Mem_Free(nv);

    item_info.img_src = ICON_EXERCISE_ADD_SPORT;
    item_info.img_bg_color = LV_COLOR_MAKE(0x00, 0xA0, 0xF0);
    item_info.prim_txt_id = WATCH_TEXT_ID_ADD_SPORT;
    item_info.sec_txt[0] = '\0';
    item_info.cb = exercise_add_sport_cb;
    item_info.type = EXERCISE_LIST_BTN_TYPE_NEXT_MENU;
    ext->add_btn = exercise_add_list_btn(NULL, ext->list, &item_info);

    exercise_add_empty_list_btn(ext->list);

    return(obj);
}

/**********************
 *   GLOBAL FUNCTIONS
 ***********************/
void exercise_create_event_cb(lv_obj_t * btn, lv_event_t event)
{
    (void)btn;
    if(LV_EVENT_CLICKED == event) {
        lv_watch_png_cache_all_free();
        lv_obj_t * obj = exercise_main_create(NULL);
        LV_ASSERT_MEM(obj);
    }
}

lv_obj_t * exercise_add_list_ui(lv_obj_t * par, uint8_t btn_num)
{
    // btn_num: the maximum number of btn to be displayed in curent screen

    lv_obj_t * list = lv_list_create(par, NULL);
    lv_list_set_scrollbar_mode(list, LV_SCROLLBAR_MODE_OFF);
    lv_obj_set_style_local_bg_opa(list, LV_LIST_PART_BG, LV_STATE_DEFAULT, LV_OPA_COVER);
    lv_obj_set_style_local_bg_color(list, LV_LIST_PART_BG, LV_STATE_DEFAULT, LV_COLOR_BLACK);
    lv_coord_t list_h = lv_obj_get_height(par);
    lv_obj_set_size(list, lv_obj_get_width(par), list_h);
    lv_coord_t font_h = lv_font_get_line_height(LV_THEME_WATCH_NIGHT_FONT_NORMAL);
    lv_coord_t list_btn_pad_ver = 2;
    lv_watch_set_free_num(list, list_btn_pad_ver);
    lv_coord_t list_btn_h = 2 * (list_btn_pad_ver + font_h);
    lv_coord_t list_pad_inner = (list_h - (btn_num * list_btn_h)) / btn_num;
    lv_obj_add_style(list, LV_LIST_PART_SCROLLABLE, &lv_style_transp_tight);
    lv_obj_set_style_local_pad_top(list, LV_LIST_PART_SCROLLABLE, LV_STATE_DEFAULT,
                                   list_pad_inner / 2);
    lv_obj_set_style_local_pad_bottom(list, LV_LIST_PART_SCROLLABLE, LV_STATE_DEFAULT,
                                      list_pad_inner / 2);
    lv_obj_set_style_local_pad_inner(list, LV_LIST_PART_SCROLLABLE, LV_STATE_DEFAULT,
                                     list_pad_inner);
    lv_obj_align(list, par, LV_ALIGN_CENTER, 0, 0);

    lv_watch_obj_add_element(list);
    lv_watch_obj_add_element(lv_page_get_scrollable(list));

    return(list);
}

lv_obj_t * exercise_add_list_btn(exercise_item_t * item, lv_obj_t * list,
                                 exercise_item_info_t * item_info)
{
    lv_obj_t * btn = lv_list_add_btn(list, NULL, NULL);
    lv_obj_add_style(btn, LV_BTN_PART_MAIN, &lv_style_transp_tight);
    lv_btn_set_fit2(btn, LV_FIT_NONE, LV_FIT_NONE);
    lv_btn_set_layout(btn, LV_LAYOUT_OFF);
    lv_coord_t btn_w = lv_obj_get_width(btn);
    if(EXERCISE_LIST_BTN_TYPE_BTN == item_info->type) {
        lv_obj_set_width(btn, btn_w * 2 / 3);
        lv_obj_set_style_local_bg_opa(btn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_70);
        lv_obj_set_style_local_bg_color(btn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_GRAY);
        lv_obj_set_style_local_bg_opa(btn, LV_BTN_PART_MAIN, LV_STATE_PRESSED, LV_OPA_70);
        lv_obj_set_style_local_bg_color(btn, LV_BTN_PART_MAIN, LV_STATE_PRESSED, LV_COLOR_GRAY);
        lv_obj_set_style_local_bg_grad_color(btn, LV_BTN_PART_MAIN, LV_STATE_PRESSED,
                                             LV_COLOR_GRAY);
        lv_obj_set_style_local_text_color(btn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_ORANGE);
        lv_obj_set_style_local_text_color(btn, LV_BTN_PART_MAIN, LV_STATE_PRESSED, LV_COLOR_WHITE);
    } else {
        lv_obj_set_style_local_bg_opa(btn, LV_BTN_PART_MAIN, LV_STATE_PRESSED, LV_OPA_0);
    }
    lv_obj_set_event_cb(btn, item_info->cb);
    lv_watch_obj_add_element(btn);

    lv_coord_t pad_ver = lv_watch_get_free_num(list);
    lv_coord_t pad_hor = btn_w / 30;
    lv_coord_t pad_inner = 2;

    lv_obj_t * label = lv_label_create(btn, NULL);
    if(EXERCISE_LIST_BTN_TYPE_BTN == item_info->type) {
        lv_obj_clean_style_list(label, LV_LABEL_PART_MAIN);
    } else {
        lv_obj_set_style_local_text_color(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                          LV_COLOR_WHITE);
    }
    lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_NORMAL);
    lv_obj_set_style_local_text_letter_space(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
    lv_label_set_long_mode(label, LV_LABEL_LONG_SROLL_CIRC);
    if(WATCH_TEXT_ID_NONE == item_info->prim_txt_id) {
        lv_label_set_text(label, "");
    } else {
        lv_label_set_text_id(label, item_info->prim_txt_id);
    }

    lv_watch_set_free_ptr(label, item);

    lv_bidi_dir_t dir = lv_obj_get_base_dir(btn);

    lv_coord_t label_h = lv_obj_get_height(label);
    if(EXERCISE_LIST_BTN_TYPE_BTN == item_info->type) {
        lv_coord_t btn_h = label_h * 3 / 2;
        lv_obj_set_height(btn, btn_h);
        lv_obj_set_style_local_radius(btn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, btn_h);
        lv_obj_align(label, btn, LV_ALIGN_CENTER, 0, 0);
        return(btn);
    } else {
        lv_obj_set_height(btn, 2 * (label_h + pad_ver));
    }

    lv_coord_t label_w = btn_w - 2 * pad_hor;
    if(item_info->img_src) {
        lv_obj_t * img_bg = lv_cont_create(btn, NULL);
        lv_obj_set_click(img_bg, false);
        if(item_info->is_opacity) {
            lv_obj_set_style_local_bg_opa(img_bg, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_COVER);
        } else {
            lv_obj_set_style_local_bg_opa(img_bg, LV_CONT_PART_MAIN, LV_STATE_DEFAULT, LV_OPA_40);
        }
        lv_obj_set_style_local_bg_color(img_bg, LV_CONT_PART_MAIN, LV_STATE_DEFAULT,
                                        item_info->img_bg_color);
        lv_obj_set_style_local_radius(img_bg, LV_CONT_PART_MAIN, LV_STATE_DEFAULT,
                                      LV_RADIUS_CIRCLE);
        lv_coord_t real_img_size = 2 * label_h;
        lv_obj_set_size(img_bg, real_img_size, real_img_size);
        if(LV_BIDI_DIR_RTL == dir) {
            lv_obj_align(img_bg, btn, LV_ALIGN_IN_RIGHT_MID, -pad_hor, 0);
        } else {
            lv_obj_align(img_bg, btn, LV_ALIGN_IN_LEFT_MID, pad_hor, 0);
        }

        lv_obj_t * img = lv_img_create(img_bg, NULL);
        lv_img_set_src(img, item_info->img_src);
        lv_coord_t img_h = lv_obj_get_height(img);
        lv_img_set_zoom(img, LV_IMG_ZOOM_NONE * real_img_size / img_h);
        lv_obj_align(img, img_bg, LV_ALIGN_CENTER, 0, 0);

        label_w -= 2 * (real_img_size + pad_inner);
    } else if(EXERCISE_LIST_BTN_TYPE_SWITCH == item_info->type) {
        label_w -= 4 * label_h;
    } else if((EXERCISE_LIST_BTN_TYPE_SETTING == item_info->type)
        || (EXERCISE_LIST_BTN_TYPE_NEXT_MENU == item_info->type)) {
        label_w -= 2 * (label_h + pad_inner);
    } else if(EXERCISE_LIST_BTN_TYPE_CHECKBOX == item_info->type) {
        label_w -= 2 * (label_h + pad_inner + 6);
    }
    lv_obj_set_width(label, label_w);

    if(0 < strlen(item_info->sec_txt)) {
        lv_obj_align(label, btn, LV_ALIGN_CENTER, 0, -lv_obj_get_height(label) / 2);

        lv_obj_t * label1 = lv_label_create(btn, label);
        lv_obj_set_style_local_text_font(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                         LV_THEME_WATCH_NIGHT_FONT_SMALL);
        lv_obj_set_style_local_text_color(label1, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                          LV_COLOR_GRAY);
        lv_label_set_text(label1, item_info->sec_txt);
        lv_obj_align(label1, label, LV_ALIGN_OUT_BOTTOM_MID, 0, 1);
    } else {
        lv_obj_align(label, btn, LV_ALIGN_CENTER, 0, 0);
    }

    if(EXERCISE_LIST_BTN_TYPE_SETTING == item_info->type) {
        lv_obj_t * btn_setting = lv_btn_create(btn, btn);
        lv_btn_set_fit2(btn_setting, LV_FIT_TIGHT, LV_FIT_NONE);
        lv_obj_add_style(btn_setting, LV_BTN_PART_MAIN, &lv_style_transp_tight);
        lv_obj_set_style_local_pad_left(btn_setting, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 2);
        lv_obj_set_style_local_pad_right(btn_setting, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, 2);

        lv_obj_t * label_setting = lv_label_create(btn_setting, NULL);
        lv_obj_set_style_local_text_font(label_setting, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                         LV_THEME_WATCH_NIGHT_FONT_BIG);
        lv_label_set_text(label_setting, LV_SYMBOL_SETTINGS);
        lv_obj_align(label_setting, btn_setting, LV_ALIGN_CENTER, 0, -1);
        if(LV_BIDI_DIR_RTL == dir) {
            lv_obj_align(btn_setting, btn, LV_ALIGN_IN_LEFT_MID, pad_hor, 0);
        } else {
            lv_obj_align(btn_setting, btn, LV_ALIGN_IN_RIGHT_MID, -pad_hor, 0);
        }
        lv_obj_set_event_cb(btn_setting, item_info->setting_cb);
        lv_watch_obj_add_element(btn_setting);
    } else if(EXERCISE_LIST_BTN_TYPE_NEXT_MENU == item_info->type) {
        lv_obj_t * label_right = lv_label_create(btn, NULL);
        lv_obj_set_style_local_text_font(label_right, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                         LV_THEME_WATCH_NIGHT_FONT_BIG);
        lv_obj_set_style_local_text_color(label_right, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                          LV_COLOR_GRAY);
        if(LV_BIDI_DIR_RTL == dir) {
            lv_label_set_text(label_right, LV_SYMBOL_LEFT);
            lv_obj_align(label_right, btn, LV_ALIGN_IN_LEFT_MID, pad_hor, -1);
        } else {
            lv_label_set_text(label_right, LV_SYMBOL_RIGHT);
            lv_obj_align(label_right, btn, LV_ALIGN_IN_RIGHT_MID, -pad_hor, -1);
        }
    } else if(EXERCISE_LIST_BTN_TYPE_SWITCH == item_info->type) {
        lv_obj_t * sw = lv_switch_create(btn, NULL);
        lv_obj_set_size(sw, 2 * label_h - pad_inner, label_h);
        lv_obj_set_style_local_bg_opa(sw, LV_SWITCH_PART_BG, LV_STATE_DEFAULT, LV_OPA_50);
        lv_obj_set_style_local_bg_color(sw, LV_SWITCH_PART_BG, LV_STATE_DEFAULT, LV_COLOR_GRAY);
        lv_coord_t knob_pad = -4;
        lv_obj_set_style_local_pad_top(sw, LV_SWITCH_PART_KNOB, LV_STATE_DEFAULT, knob_pad);
        lv_obj_set_style_local_pad_bottom(sw, LV_SWITCH_PART_KNOB, LV_STATE_DEFAULT, knob_pad);
        lv_obj_set_style_local_pad_left(sw, LV_SWITCH_PART_KNOB, LV_STATE_DEFAULT, knob_pad);
        lv_obj_set_style_local_pad_right(sw, LV_SWITCH_PART_KNOB, LV_STATE_DEFAULT, knob_pad);
        lv_obj_set_style_local_bg_color(sw, LV_SWITCH_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_ORANGE);
        if(item_info->is_on) {
            lv_switch_on(sw, LV_ANIM_OFF);
        } else {
            lv_switch_off(sw, LV_ANIM_OFF);
        }
        lv_obj_set_event_cb(sw, item_info->setting_cb);
        if(LV_BIDI_DIR_RTL == dir) {
            lv_obj_align(sw, btn, LV_ALIGN_IN_LEFT_MID, pad_hor + 2, 0);
        } else {
            lv_obj_align(sw, btn, LV_ALIGN_IN_RIGHT_MID, -pad_hor - 2, 0);
        }
    } else if(EXERCISE_LIST_BTN_TYPE_CHECKBOX == item_info->type) {
        lv_obj_t * cb = lv_checkbox_create(btn, NULL);
        lv_checkbox_set_checked(cb, item_info->is_on);
        lv_checkbox_set_text(cb, "");
        lv_obj_clean_style_list(cb, LV_CHECKBOX_PART_BULLET);
        lv_obj_add_style(cb, LV_CHECKBOX_PART_BULLET, &lv_watch_cb_styles);
        lv_obj_set_style_local_text_font(cb, LV_CHECKBOX_PART_BG, LV_STATE_DEFAULT,
                                         LV_THEME_WATCH_NIGHT_FONT_NORMAL);
        lv_coord_t cb_size = label_h;
        lv_obj_set_style_local_border_width(cb, LV_CHECKBOX_PART_BULLET, LV_STATE_DEFAULT,
                                            cb_size / 10);
        lv_obj_set_style_local_bg_color(cb, LV_CHECKBOX_PART_BULLET, LV_STATE_DEFAULT,
                                        LV_COLOR_BLACK);
        lv_obj_set_style_local_border_width(cb, LV_CHECKBOX_PART_BULLET, LV_STATE_CHECKED,
                                            cb_size / 4);
        lv_obj_set_style_local_border_color(cb, LV_CHECKBOX_PART_BULLET, LV_STATE_CHECKED,
                                            LV_COLOR_ORANGE);
        lv_obj_set_style_local_bg_color(cb, LV_CHECKBOX_PART_BULLET, LV_STATE_CHECKED,
                                        LV_COLOR_WHITE);
        lv_obj_set_click(cb, false);
        if(LV_BIDI_DIR_RTL == dir) {
            lv_obj_align(cb, btn, LV_ALIGN_IN_LEFT_MID, pad_hor, 0);
        } else {
            lv_obj_align(cb, btn, LV_ALIGN_IN_RIGHT_MID, -pad_hor, 0);
        }
    }

    return(btn);
}

lv_obj_t * exercise_add_list_btn_with_long_txt(lv_obj_t * list, char * txt)
{
    printf("%s\n", __FUNCTION__);

    lv_obj_t * btn = lv_list_add_btn(list, NULL, NULL);
    lv_obj_add_style(btn, LV_BTN_PART_MAIN, &lv_style_transp_tight);
    lv_coord_t pad_ver = lv_watch_get_free_num(list);
    lv_obj_set_style_local_pad_top(btn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, pad_ver);
    lv_obj_set_style_local_pad_bottom(btn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, pad_ver);
    lv_coord_t pad_hor = lv_obj_get_width(btn) / 30;
    lv_obj_set_style_local_pad_left(btn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, pad_hor);
    lv_obj_set_style_local_pad_right(btn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, pad_hor);
    lv_obj_set_click(btn, false);

    lv_obj_t * label = lv_label_create(btn, NULL);
    lv_obj_set_style_local_text_font(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT,
                                     LV_THEME_WATCH_NIGHT_FONT_NORMAL);
    lv_obj_set_style_local_text_color(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, LV_COLOR_WHITE);
    lv_obj_set_style_local_text_letter_space(label, LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, 0);
    lv_label_set_align(label, LV_LABEL_ALIGN_CENTER);
    lv_label_set_long_mode(label, LV_LABEL_LONG_BREAK);
    lv_label_set_text(label, txt);

    return(btn);
}

void exercise_add_empty_list_btn(lv_obj_t * list)
{
    exercise_item_info_t item_info = {};
    item_info.prim_txt_id = WATCH_TEXT_ID_NONE;
    exercise_add_list_btn(NULL, list, &item_info);
}

void exercise_get_goals_txt(exercise_nv_goals_t * nv, char * txt, uint32_t txt_len)
{
    if((NULL == nv) || (NULL == txt) || (0 == txt_len)) return;

    if(0 == nv->value) {
        snprintf(txt, txt_len, "%s", (char *)lv_lang_get_text(WATCH_TEXT_ID_GOALLESSNESS));
    } else if(EXERCISE_VALUE_TYPE_DISTANCE == nv->type) {
        if(EXERCISE_GOALS_DISTANCE_HALF_MARATHON == nv->value) {
            snprintf(txt, txt_len, "%s", (char *)lv_lang_get_text(WATCH_TEXT_ID_HALF_MARATHON));
        } else if(EXERCISE_GOALS_DISTANCE_MARATHON == nv->value) {
            snprintf(txt, txt_len, "%s", (char *)lv_lang_get_text(WATCH_TEXT_ID_MARATHON));
        } else {
            snprintf(txt, txt_len, "%d%s", nv->value,
                     (char *)lv_lang_get_text(WATCH_TEXT_ID_KILOMETRE));
        }
    } else if(EXERCISE_VALUE_TYPE_TIME == nv->type) {
        if(1 < nv->value) {
            snprintf(txt, txt_len, "%d%s", nv->value,
                     (char *)lv_lang_get_text(WATCH_TEXT_ID_ACT_MINS));
        } else {
            snprintf(txt, txt_len, "%d%s", nv->value,
                     (char *)lv_lang_get_text(WATCH_TEXT_ID_ACT_MIN));
        }
    } else if(EXERCISE_VALUE_TYPE_CALORIE == nv->type) {
        snprintf(txt, txt_len, "%d%s", nv->value,
                 (char *)lv_lang_get_text(WATCH_TEXT_ID_KILOCALORIE));
    }
}

void exercise_read_nv(EXERCISE_TYPE_T type, uint8_t ** data, uint32_t * data_size)
{
    if((NULL == data) && (NULL == data_size)) return;

    char path[30] = {};
    snprintf(path, 30, "%s%d%s", EXERCISE_NV_PATH_PREFIX, type, EXERCISE_NV_PATH_SUFFIX);

    lv_fs_file_t file = {};
    lv_fs_open(&file, path, LV_FS_MODE_RD);
    if(file.file_d) {
        uint32_t size;
        lv_fs_size(&file, &size);
        if(data_size) *data_size = size;
        if((0 < size) && data) {
            *data = Hal_Mem_Alloc(size);
            lv_fs_seek(&file, 0);
            lv_fs_read(&file, *data, size, NULL);
        };
    } else {
        if(data) *data = NULL;
        if(data_size) *data_size = 0;
    }
    lv_fs_close(&file);
}

void exercise_write_nv(EXERCISE_TYPE_T type, uint32_t offset, uint32_t size,
                       void * data, EXERCISE_WRITE_NV_MODE_T mode)
{
    char path[30] = {};
    snprintf(path, 30, "%s%d%s", EXERCISE_NV_PATH_PREFIX, type, EXERCISE_NV_PATH_SUFFIX);
    printf("%s: type %d, offset %d, size %d, mode %d, path %s\n",
           __FUNCTION__, type, offset, size, mode, path);

    if((NULL == data) || (0 == size)) {
        return;
    }

    lv_fs_file_t file = {};
    if(EXERCISE_WRITE_NV_MODE_RESET == mode) {
        lv_fs_open(&file, path, LV_FS_MODE_WR);
    } else if(EXERCISE_WRITE_NV_MODE_REPLACE == mode) {
        lv_fs_open(&file, path, LV_FS_MODE_RD | LV_FS_MODE_APPEND);
        lv_fs_seek(&file, offset);
    } else {
        lv_fs_open(&file, path, LV_FS_MODE_WR | LV_FS_MODE_APPEND);
    }
    lv_fs_write(&file, data, size, NULL);
    lv_fs_close(&file);
}

uint32_t exercise_get_nv_offset(uint8_t setting_item_bitmap, EXERCISE_SETTING_ITEM_TYPE_T type)
{
    if(0 == (setting_item_bitmap & (1 << type))) {
        return(0xFFFFFFFF);
    }

    uint32_t offset = 0;
    for(uint8_t i = 0;i < EXERCISE_MAX_SETTING_ITEM_NUM;i++) {
        if(type == i) break;

        if(0 != (setting_item_bitmap & (1 << i))) {
            switch(i) {
            case EXERCISE_SETTING_ITEM_TYPE_SMART_COACH:
                offset += sizeof(exercise_nv_smart_coach_t);
                break;
            case EXERCISE_SETTING_ITEM_TYPE_GOALS:
                offset += sizeof(exercise_nv_goals_t);
                break;
            case EXERCISE_SETTING_ITEM_TYPE_REMINDER:
                offset += sizeof(exercise_nv_reminder_t);
                break;
            case EXERCISE_SETTING_ITEM_TYPE_INTEL_RUN_COMP:
                offset += sizeof(exercise_nv_intel_run_comp_t);
                break;
            }
        }
    }

    return(offset);
}

uint8_t * exercise_get_nv_pointer(uint8_t * nv_data, uint8_t setting_item_bitmap,
                                  EXERCISE_SETTING_ITEM_TYPE_T type)
{
    if(NULL == nv_data) {
        printf("%s: warning, nv_data is NULL\n", __FUNCTION__);
        return(NULL);
    }

    uint32_t offset = exercise_get_nv_offset(setting_item_bitmap, type);
    if(0xFFFFFFFF != offset) {
        return(&nv_data[offset]);
    } else {
        return(NULL);
    }
}

void * exercise_get_ext(lv_watch_Activity_Id_t id)
{
    lv_obj_t * activity_obj = lv_watch_get_activity_obj(id);
    if(NULL == activity_obj) return NULL;

    lv_obj_t * obj = NULL;
    lv_watch_get_child_obj(activity_obj, lv_watch_obj_signal, &obj);
    if(NULL == obj) return NULL;

    if(obj) {
        void * ext = lv_obj_get_ext_attr(obj);
        return ext;
    }
    return NULL;
}

void exercise_del_ui(lv_watch_Activity_Id_t id)
{
    lv_obj_t * activity_obj = lv_watch_get_activity_obj(id);
    if(NULL == activity_obj) return;

    lv_watch_activity_ext_t * ext = lv_obj_get_ext_attr(activity_obj);
    LV_ASSERT_MEM(ext);
    if(ext->prepare_destory) {
        ext->prepare_destory(activity_obj);
    }
    lv_obj_del(activity_obj);

    return;
}

void exercise_int_to_str(uint32_t value, char * str, uint32_t str_len)
{
    uint8_t num = 1;
    uint32_t temp = value;
    while(9 < temp) {
        num++;
        temp /= 10;
    }
    if(str_len < (num + 1)) {
        printf("%s: warning, str_len %d < num %d + 1\n", __FUNCTION__, str_len, num);
        return;
    }
    Hal_Mem_Set(str, 0, str_len);
    for(uint8_t i = 0;i < num;i++) {
        temp = value;
        for(uint8_t j = 0;j < (num - 1 - i);j++) {
            temp /= 10;
        }
        str[i] = (temp % 10) + '0';
    }
}

int32_t exercise_str_to_int(char * txt)
{
    if(NULL == txt) {
        printf("%s: warning, txt is NULL\n", __FUNCTION__);
        return(-1);
    }
    uint8_t len = strlen(txt);
    if(0 == len) {
        printf("%s: warning, the len of txt is 0\n", __FUNCTION__);
        return(-1);
    }
    if(1 == len) {
        return(txt[0] - '0');
    }
    uint32_t digit;
    uint32_t value = 0;
    for(uint8_t i = 0;i < len;i++) {
        digit = txt[i] - '0';
        for(uint8_t j = 0;j < (len - 1 - i);j++) {
            digit *= 10;
        }
        value += digit;
    }
    return(value);
}

void exercise_com_prepare_destroy(lv_obj_t * activity_obj)
{
    printf("%s\n", __FUNCTION__);
    lv_watch_png_cache_all_free();
}

void exercise_start_recovery_time_anim(exercise_ext_t * ext)
{
    lv_anim_t a;
    lv_anim_init(&a);
    lv_anim_set_var(&a, ext->recovery_time_label);
    lv_anim_set_start_cb(&a, exercise_recovery_time_anim_cb);
    lv_anim_set_time(&a, 60000);
    lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
    lv_anim_start(&a);
}

#endif /*USE_LV_WATCH_EXERCISE*/
